using System;
using System.Collections;
using System.Reflection;
using UnityEngine;
using YooAsset;

namespace shaco
{
    public class YooAssetPackageUpdater
    {
        public System.Action<string> onDownloadErrorCallback = null;
        public System.Action<float, string> onDownloadProgressCallback = null;
        public System.Action onDownloadEndCallBack = null;

        public string currentPackageName => _currentPackageName;
        string _currentPackageName = "";

        //本地编辑器开发模式
        public IEnumerator StartWithEditorSimulateMode(string packageName = "DefaultPackage")
        {
#if UNITY_EDITOR
            var simulateBuildParam = new EditorSimulateBuildParam();
            simulateBuildParam.PackageName = packageName;
            var simulateBuildResult = EditorSimulateModeHelper.SimulateBuild(simulateBuildParam);
            // simulateBuildResult.PackageRootDirectory = ResourcesFolderPath;
            var editorFileSystem = FileSystemParameters.CreateDefaultEditorFileSystemParameters(simulateBuildResult);
            var parametersTmp = new EditorSimulateModeParameters();
            parametersTmp.EditorFileSystemParameters = editorFileSystem;
            yield return Coroutine_Updating(packageName, parametersTmp);
#else
            throw new System.NotImplementedException("本地开发模式仅能在编辑器上运行");
#endif
        }

        //离线整包模式，资源在StreamingAsset目录
        public IEnumerator StartWithOfflinePlayMode(string packageName = "DefaultPackage", IDecryptionServices decryptionServices = null, EFileVerifyLevel verifyLevel = EFileVerifyLevel.Middle)
        {
            var buildinFileSystem = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(decryptionServices, verifyLevel);
            var parametersTmp = new OfflinePlayModeParameters();
            parametersTmp.BuildinFileSystemParameters = buildinFileSystem;
            yield return Coroutine_Updating(packageName, parametersTmp);
        }

        //在线下载模式
        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        //注意：需要手动拷贝YooAsset在SimulateBuild下导出的几个manifest文件到StreamingAssets/yoo/{包名称,例如DefaultPackage}目录中
        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        public IEnumerator StartWithHostPlayMode(string hostServer, string fallbackServer, string packageName = "DefaultPackage", IDecryptionServices decryptionServices = null, EFileVerifyLevel verifyLevel = EFileVerifyLevel.Middle)
        {
            var parametersTmp = new YooAsset.HostPlayModeParameters();
            parametersTmp.BuildinFileSystemParameters = null;
            var remoteServices = new RemoteServices(hostServer, fallbackServer);
            parametersTmp.CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices, decryptionServices, verifyLevel);
            yield return Coroutine_Updating(packageName, parametersTmp);
        }

        //包含本地StreamingAsset目录的在线下载模式
        public IEnumerator StartWithHostPlayMode_WithBuildin(string hostServer, string fallbackServer, string packageName = "DefaultPackage", IDecryptionServices decryptionServices = null, EFileVerifyLevel verifyLevel = EFileVerifyLevel.Middle)
        {
            var parametersTmp = new YooAsset.HostPlayModeParameters();
            parametersTmp.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(decryptionServices, EFileVerifyLevel.Low);
            var remoteServices = new RemoteServices(hostServer, fallbackServer);
            parametersTmp.CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices, decryptionServices, verifyLevel);
            yield return Coroutine_Updating(packageName, parametersTmp);
        }

        //webgl模式
        public IEnumerator StartWithWebPlayMode(string packageName = "DefaultPackage")
        {
            var parametersTmp = new WebPlayModeParameters();
            parametersTmp.WebServerFileSystemParameters = FileSystemParameters.CreateDefaultWebServerFileSystemParameters();
            yield return Coroutine_Updating(packageName, parametersTmp);
        }

        //微信小游戏模式
        public IEnumerator StartWithWeixinMiniGamePlayMode(string hostServer, string fallbackServer, string packageName = "DefaultPackage")
        {
            var parametersTmp = new WebPlayModeParameters();
#if UNITY_WEBGL && WEIXINMINIGAME && !UNITY_EDITOR
            var remoteServices = new RemoteServices(hostServer, fallbackServer);
            parametersTmp.WebServerFileSystemParameters = WechatFileSystemCreater.CreateWechatFileSystemParameters(remoteServices);
            yield return Coroutine_Updating(packageName, parametersTmp);
#else
            throw new System.NotImplementedException("该模式仅能在微信小游戏运行");
#endif
        }

        /// <summary>
        /// 远端资源地址查询服务类
        /// </summary>
        private class RemoteServices : IRemoteServices
        {
            private readonly string _defaultHostServer;
            private readonly string _fallbackHostServer;

            public RemoteServices(string defaultHostServer, string fallbackHostServer)
            {
                _defaultHostServer = defaultHostServer;
                _fallbackHostServer = fallbackHostServer;
            }
            string IRemoteServices.GetRemoteMainURL(string fileName)
            {
                return $"{_defaultHostServer}/{fileName}";
            }
            string IRemoteServices.GetRemoteFallbackURL(string fileName)
            {
                return $"{_fallbackHostServer}/{fileName}";
            }
        }

        static bool _isInited = false;
        IEnumerator Coroutine_Updating(string packageName, InitializeParameters initParameters)
        {
            Debug.Log($"<color=green> 开始加载资源包: {packageName} 参数类型{initParameters.GetType()} </color>");

            if (!_isInited)
            {
                YooAsset.YooAssets.Initialize();
                _isInited = true;
            }

            _currentPackageName = packageName;
            var package = YooAsset.YooAssets.TryGetPackage(packageName);
            if (null == package)
                package = YooAsset.YooAssets.CreatePackage(packageName);
            YooAsset.YooAssets.SetDefaultPackage(package);
            SetLoadingPercent(0.1f);

            //初始化参数            
            var request = package.InitializeAsync(initParameters);
            yield return request;

            if (request.Status == EOperationStatus.Succeed)
            {
                Debug.Log($"{package.PackageName}初始化资源参数成功");
            }
            else
            {
                Debug.LogError($"{package.PackageName}初始化资源参数失败, state=" + request.Status + " error=" + request.Error);
                ForceStopUpdatingCoroutine("Net Error - 1");
            }

            //更新版本号
            SetLoadingPercent(0.2f);
            yield return UpdatePackageVersion(package, initParameters);
            SetLoadingPercent(0.4f);

            //下载资源
            yield return UpdateDownload(package);

            if (null != onDownloadEndCallBack)
                onDownloadEndCallBack();
        }

        IEnumerator UpdatePackageVersion(ResourcePackage package, InitializeParameters initParameters)
        {
            RequestPackageVersionOperation operation = package.RequestPackageVersionAsync();
            if (null == operation)
            {
                Debug.LogError($"{package.PackageName}创建资源版本号请求失败");
                ForceStopUpdatingCoroutine("Net Error - 2.1");
                yield break;
            }

            yield return operation;

            if (operation.Status == EOperationStatus.Succeed)
            {
                var packVersion = operation.PackageVersion;
                Debug.Log($"{package.PackageName}版本号:" + packVersion);

                //更新manifest资源清单
                SetLoadingPercent(0.3f);
                yield return UpdatePackageManifest(package, packVersion);
            }
            else
            {
                Debug.LogError($"{package.PackageName}获取资源版本号失败");
                ForceStopUpdatingCoroutine("Net Error - 2.2");
            }
        }

        IEnumerator UpdatePackageManifest(ResourcePackage package, string packVersion)
        {
            // 更新成功后自动保存版本号，作为下次初始化的版本。
            // 也可以通过operation.SavePackageVersion()方法保存。
            var operation = package.UpdatePackageManifestAsync(packVersion);
            yield return operation;

            if (operation.Status == EOperationStatus.Succeed)
            {
                //更新成功
                Debug.Log($"{package.PackageName}Updated manifest success");

                //删除旧的manifest文件
                yield return package.ClearUnusedManifestFilesAsync();
            }
            else
            {
                //更新失败
                ForceStopUpdatingCoroutine(operation.Error);
            }
        }

        IEnumerator UpdateDownload(ResourcePackage package)
        {
            int downloadingMaxNum = 10;
            int failedTryAgain = 3;
            var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);
            Debug.Log($"{package.PackageName}需要下载的资源数量 " + downloader.TotalDownloadCount);

            //没有需要下载的资源
            if (downloader.TotalDownloadCount == 0)
            {
                yield break;
            }

            //需要下载的文件总数和总大小
            int totalDownloadCount = downloader.TotalDownloadCount;
            long totalDownloadBytes = downloader.TotalDownloadBytes;

            //注册回调方法
            downloader.DownloadErrorCallback = OnDownloadErrorFunction;
            downloader.DownloadUpdateCallback = OnDownloadProgressUpdateFunction;
            // downloader.OnDownloadOverCallback = OnDownloadOverFunction;
            // downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction;

            //开启下载
            downloader.BeginDownload();
            yield return downloader;

            //检测下载结果
            if (downloader.Status == EOperationStatus.Succeed)
            {
                //下载成功
                Debug.Log($"{package.PackageName}下载完成");
                var operation = package.ClearCacheBundleFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
                yield return operation;

                if (operation.Status == EOperationStatus.Succeed)
                {
                    Debug.Log($"{package.PackageName}下载完成后清零无用的缓存文件，成功");
                }
                else
                {
                    Debug.LogError($"{package.PackageName}下载完成后清零无用的缓存文件，失败 " + package.GetPackageVersion());
                }
            }
            else
            {
                //下载失败
                Debug.Log($"{package.PackageName}下载失败");
                ForceStopUpdatingCoroutine($"{package.PackageName}Net Error - 4\n");
            }
        }

        void OnDownloadErrorFunction(DownloadErrorData data)
        {
            Debug.LogError($"{data.PackageName}下载 {data.FileName} 发生错误 {data.ErrorInfo}");
            ForceStopUpdatingCoroutine("Net Error - 3\n" + data.ErrorInfo);
        }

        void OnDownloadProgressUpdateFunction(DownloadUpdateData data)
        {
            var downloadPercent = (double)data.CurrentDownloadBytes / data.TotalDownloadBytes;
            var downloadStringCurrent = GetSizeString(data.CurrentDownloadBytes);
            var downloadStringTotal = GetSizeString(data.TotalDownloadBytes);
            SetLoadingPercent(0.6f * (float)downloadPercent, $" ({downloadStringCurrent}/{downloadStringTotal})");
        }

        void ForceStopUpdatingCoroutine(string error)
        {
            if (null != onDownloadErrorCallback)
                onDownloadErrorCallback(error);

            throw new System.Exception("YooAssetPackageUpdater error: " + error);
        }
        
        void SetLoadingPercent(float percent, string descryption = "")
        {
            if (null != onDownloadProgressCallback)
                onDownloadProgressCallback(percent, descryption);
        }

        private string GetSizeString(long size)
        {
            if (size < 1024L)
            {
                return string.Format("{0} Bytes", size.ToString());
            }

            if (size < 1024L * 1024L)
            {
                return string.Format("{0} KB", (size / 1024f).ToString("F2"));
            }

            if (size < 1024L * 1024L * 1024L)
            {
                return string.Format("{0} MB", (size / 1024f / 1024f).ToString("F2"));
            }

            if (size < 1024L * 1024L * 1024L * 1024L)
            {
                return string.Format("{0} GB", (size / 1024f / 1024f / 1024f).ToString("F2"));
            }

            return string.Format("{0} TB", (size / 1024f / 1024f / 1024f / 1024f).ToString("F2"));
        }
    }
}